<?php
// +----------------------------------------------------------------------
// | 日历相关 服务
// +----------------------------------------------------------------------
// | 主要服务于日历 TimeLog 数据处理
// +----------------------------------------------------------------------
// | 错误编码头 228xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\model\CalendarModel;
use common\model\EntityModel;
use common\model\ProjectModel;
use common\model\ScheduleManageModel;
use common\model\StepCategoryModel;
use support\ErrorCode;
use think\Exception;
use think\module\Module;

class ScheduleService
{
    private $entityModel;
    private $projectModel;
    private $stepCategoryModel;

    public function __construct()
    {
        $this->entityModel = model(EntityModel::class);
        $this->projectModel = model(ProjectModel::class);
        $this->stepCategoryModel = model(StepCategoryModel::class);
    }

    /**
     * 新增日历事项
     * @param $param
     * @return array
     */
    public function addCalendar($param)
    {
        $calendarModel = model(CalendarModel::class);
        $resData = $calendarModel->addItem($param);
        if (!$resData) {
            // 添加日历事项失败错误码 004
            throw_strack_exception($calendarModel->getError(), ErrorCode::CALENDAR_CREATE_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 修改日历事项
     * @param $param
     * @return array
     */
    public function modifyCalendar($param)
    {
        $calendarModel = model(CalendarModel::class);
        $resData = $calendarModel->modifyItem($param);
        if (!$resData) {
            // 修改日历事项失败错误码 005
            throw_strack_exception($calendarModel->getError(), ErrorCode::CALENDAR_MODIFY_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 删除日历事项
     * @param $param
     * @return array
     */
    public function deleteCalendar($param)
    {
        $calendarModel = model(CalendarModel::class);
        $resData = $calendarModel->deleteItem($param);
        if (!$resData) {
            // 删除日历事项失败错误码 006
            throw_strack_exception($calendarModel->getError(), ErrorCode::CALENDAR_DELETE_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }


    /**
     * 日历事项列表数据
     * @param $param
     * @return mixed
     */
    public function getCalendarGridData($param)
    {
        $options = [
            'page' => [$param["page"], $param["rows"]]
        ];
        $calendarModel = model(CalendarModel::class);
        $calendarData = $calendarModel->selectData($options);
        return $calendarData;
    }

    /**
     * 集排期信息
     * @param $projectId
     * @param int $getTenantId
     * @return array
     * @throws Exception
     */
    public function getEpisodeSchedule($projectId, int $getTenantId)
    {
        $project = $this->projectModel->field('id,name,start_time,end_time')->find($projectId);
        if (empty($project)) {
            throw new Exception("project not found", ErrorCode::PROJECT_NOT_FOUND);
        }
        $episodeList = $this->entityModel->where(['tenant_id' => $getTenantId, 'project_id' => $projectId, 'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id']])
            ->field("id,name,code,'episode' as row_type")->select();
        $scheduleList = $this->entityModel->alias('e')->join('task t ON t.entity_id = e.id')
            ->where(['t.project_id' => $projectId, 't.plan_start_time' => ['GT', 0], 't.plan_end_time' => ['GT', 0]])
            ->field("e.initial_episode_code as episode_code,MAX(t.plan_end_time) as end_time,MIN(t.plan_start_time) as start_time")
            ->group("e.initial_episode_code")->select();
        $scheduleList = array_column($scheduleList, null, 'episode_code');
        foreach ($episodeList as &$episode) {
            $schedule = $scheduleList[$episode['code']] ?? null;
            if (is_null($schedule)) {
                $episode['no_schedule'] = true;
            } else {
                $episode['start_time'] = $schedule['start_time'];
                $episode['end_time'] = $schedule['end_time'];
            }
            StepService::formatGanttItem($episode);
        }
        return StepService::formatStepCategoryPlanData($episodeList, $project);
    }

    /**
     * 集分类排期信息
     * @param $projectId
     * @param $episodeId
     * @return array
     * @throws Exception
     */
    public function getEpisodeStepCategorySchedule($projectId, $episodeId)
    {
        $episode = $this->entityModel->find($episodeId);
        if (empty($episode)) {
            throw new Exception("episode not found", ErrorCode::MISSING_EPISODE);
        }
        $mergeModuleCodesConfig = OptionsService::getInstance()->getOptionsData('merge_module_config');
        $moduleIdList = [];
        $mergeModuleMap = [];
        $showStepCategoryModuleIdList = [];
        if (empty($mergeModuleCodesConfig)) {
            throw new Exception("missing config merge_module_config", ErrorCode::MISSING_CONFIG);
        }
        foreach ($mergeModuleCodesConfig as $k => $config) {
            $showStepCategoryModuleIdList[] = Module::$moduleDictData['module_index_by_code'][$k]['id'];
            $mergeModuleCodes = explode(',', $config);
            foreach ($mergeModuleCodes as $mergeModuleCode) {
                $moduleIdList[] = Module::$moduleDictData['module_index_by_code'][$mergeModuleCode]['id'];
                $mergeModuleMap[Module::$moduleDictData['module_index_by_code'][$mergeModuleCode]['id']] = $k;
            }
        }
        $stepCategoryList = $this->stepCategoryModel
            ->where([
                'entity_id' => ['IN', implode(',', $showStepCategoryModuleIdList)],
                'parent_id' => 0,
            ])
            ->field("id,name,code,0 as start_time,0 as end_time,'step_category' as row_type")
            ->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'code');
        $scheduleList = $this->entityModel->alias('e')->join('task t ON t.entity_id = e.id')
            ->join('step_category sc ON sc.entity_id = e.module_id')
            ->where(['e.project_id' => $projectId, 'e.module_id' => ['IN', implode(',', $moduleIdList)], 'e.initial_episode_code' => $episode['code'], 't.plan_start_time' => ['GT', 0], 't.plan_end_time' => ['GT', 0]])
            ->field("sc.id as id,sc.name as name,e.module_id,MAX(t.plan_end_time) as end_time,MIN(t.plan_start_time) as start_time")
            ->group("e.module_id")->select();
        $projectData = [];
        if (count($scheduleList) > 0) {
            $scheduleList = array_column($scheduleList, null, 'module_id');
            $projectData = ['start_time' => 0, 'end_time' => 0];
            foreach ($scheduleList as $moduleId => $schedule) {
                $belongModuleCode = $mergeModuleMap[$moduleId];
                if ($stepCategoryList[$belongModuleCode]['start_time'] == 0 || $schedule['start_time'] < $stepCategoryList[$belongModuleCode]['start_time']) {
                    $stepCategoryList[$belongModuleCode]['start_time'] = $schedule['start_time'];
                }
                if ($stepCategoryList[$belongModuleCode]['end_time'] == 0 || $schedule['end_time'] > $stepCategoryList[$belongModuleCode]['end_time']) {
                    $stepCategoryList[$belongModuleCode]['end_time'] = $schedule['end_time'];
                }
            }
            $projectData['start_time'] = min(array_column($stepCategoryList, 'start_time'));
            $projectData['end_time'] = max(array_column($stepCategoryList, 'end_time'));
            foreach ($stepCategoryList as &$stepCategory) {
                StepService::formatGanttItem($stepCategory);
            }
        }
        return StepService::formatStepCategoryPlanData(array_values($stepCategoryList), $projectData);
    }

    /**
     * 一集的实体同一合并分类下 排期数据
     * @param $param
     * @return array
     * @throws Exception
     */
    public function getEpisodeStepCategoryEntityTaskSchedule($param)
    {
        $projectId = $param['filter']['project_id'];
        $episodeId = $param['filter']['episode_id'];
        $moduleCode = $param['filter']['code'];
        $episode = $this->entityModel->find($episodeId);
        if (empty($episode)) {
            throw new Exception("episode not found", ErrorCode::MISSING_EPISODE);
        }
        $mergeModuleCodesConfig = OptionsService::getInstance()->getOptionsConfigItemData('merge_module_config', $moduleCode);
        if (empty($mergeModuleCodesConfig)) {
            throw new Exception("missing config merge_module_config $moduleCode", ErrorCode::MISSING_CONFIG);
        }
        $mergeModuleCodes = explode(',', $mergeModuleCodesConfig);
        $entityFilter = [
            'entity.project_id' => $projectId,
            'entity.initial_episode_code' => $episode['code'],
            'task.step_type' => 'step'
        ];

        if ($moduleCode === 'asset') { //资产筛选条件
            $gradeSearch = $param['filter']['grade'] ?? [];
            $moduleSearchList = $param['filter']['module_code'] ?? [];
            $categoryNameSearch = $param['filter']['category_name'] ?? [];

            if (!empty($gradeSearch)) {
                $entityFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_grade')))"] = $gradeSearch;
            }
            if (!empty($categoryNameSearch)) {
                $entityFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_category')))"] = $categoryNameSearch;
            }
            if (!empty($moduleSearchList)) {
                $mergeModuleCodes = array_filter($mergeModuleCodes, function ($item) use ($moduleSearchList) {
                    return in_array($item, $moduleSearchList);
                });
            }
        }


        $moduleIdList = [];
        foreach ($mergeModuleCodes as $mergeModuleCode) { //类型筛选
            $moduleIdList[] = Module::$moduleDictData['module_index_by_code'][$mergeModuleCode]['id'];
        }
        $entityFilter['entity.module_id'] = ['IN', implode(',', $moduleIdList)];

        //名字筛选
        $entityNameSearch = $param['filter']['entity_name'] ?? [];

        if (!empty($entityNameSearch)) {
            $entityFilter['entity.name'] = $entityNameSearch;
        }


        $queryFields = [
            "entity.id",
            "entity.name",
            "entity.code",
            "entity.showings_number",
            "entity.module_id",
            "entity.entity_id",
            "entity.entity_module_id",
            "task.id",
            "task.name",
            "task.code",
            "task.entity_id",
            "task.step_category_id",
            "task.plan_start_time",
            "task.plan_end_time",
            "task.assignee",
            "user.id",
            "user.name",
        ];
        $order = 'entity.name ASC';
        $scheduleList = $this->entityModel
            ->join('module ON module.id = entity.module_id')->join('task ON task.entity_id = entity.id')->join('user ON user.id = task.assignee', 'LEFT')
            ->where($entityFilter)
            ->field(build_complex_query_fields($queryFields, '__'))
            ->order($order)
            ->select();

        $stepCategoryList = $taskStepCategoryList = [];
        if (count($scheduleList) > 0) {
            $stepCategoryList = $this->stepCategoryModel
                ->where([
                    'entity_id' => ['IN', array_column($scheduleList, 'entity__module_id', 'entity__module_id')],
                    'parent_id' => 0,
                ])
                ->field('id,name,code,entity_id')->select();
            $stepCategoryList = array_column($stepCategoryList, null, 'entity_id');

            $taskStepCategoryList = $this->stepCategoryModel->where(['id' => ['IN', array_column($scheduleList, 'task__step_category_id', 'task__step_category_id')]])->field('id,name,code,entity_id')->select();
            $taskStepCategoryList = array_column($taskStepCategoryList, null, 'id');
        }

        // 格式化数据
        $res = [];
        if ($moduleCode === 'shot') {
            $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];
            $sequenceModuleId = Module::$moduleDictData['module_index_by_code']['sequence']['id'];
            $scheduleList = array_group_by($scheduleList, 'entity__module_id');
            // 当前场的 序列任务和镜头任务数据
            $sequenceTaskList = $scheduleList[$sequenceModuleId] ?? [];
            $shotTaskList = $scheduleList[$shotModuleId] ?? [];
            // 通过序列分组
            $shotTaskListGroupedByEntityId = array_group_by($shotTaskList, 'entity__entity_id');
            $sequenceList = array_group_by($sequenceTaskList, 'entity__id');
            foreach ($sequenceList as $sequenceId => $sequenceTaskList) {
                $tmpSequence = [
                    'id' => 'sequence_' . $sequenceId,
                    'name' => $sequenceTaskList[0]['entity__name'],
                    'code' => $sequenceTaskList[0]['entity__code'],
                    'expanded' => false,
                    'row_type' => 'entity',
                ];
                StepService::formatGanttItem($tmpSequence);
                $shotTaskList = $shotTaskListGroupedByEntityId[$sequenceId] ?? [];
                $taskListGroupByStepCategory = array_group_by(array_merge($sequenceTaskList, $shotTaskList), 'task__step_category_id');
                foreach ($taskListGroupByStepCategory as $stepCategoryId => $currentTaskList) {
                    $tmpChildrenStepCategory = [
                        'id' => $sequenceId . 'step_category' . $stepCategoryId,
                        'name' => $taskStepCategoryList[$stepCategoryId]['name'] ?? 'None',
                        'code' => $taskStepCategoryList[$stepCategoryId]['code'] ?? 'None',
                        'start_time' => min(array_column($currentTaskList, 'task__plan_start_time')),
                        'end_time' => max(array_column($currentTaskList, 'task__plan_end_time')),
                        'row_type' => 'step_category',
                    ];
                    StepService::formatGanttItem($tmpChildrenStepCategory);
                    $tmpSequence['children'][] = $tmpChildrenStepCategory;
                }
                $res[] = $tmpSequence;
            }
        } else if ($moduleCode === 'asset') {
            $scheduleList = array_group_by($scheduleList, 'entity__id');
            $res = [];
            foreach ($scheduleList as $entityId => $taskList) {
                $entityData = [
                    'id' => 'entity_' . $entityId,
                    'real_id' => $entityId,
                    'name' => $taskList[0]['entity__name'],
                    'step_name' => $stepCategoryList[$taskList[0]['entity__module_id']]['name'],
                    'expanded' => false,
                    'row_type' => 'entity',
                ];
                StepService::formatGanttItem($entityData, null, '', false);
                $childrenTask = [];
                foreach ($taskList as $taskItem) {
                    $tmpChildrenTask = [
                        'id' => 'task_' . $taskItem['task__id'],
                        'name' => $taskItem['task__name'],
                        'code' => $taskItem['task__code'],
                        'executor_name' => $taskItem['user__name'],
                        'start_time' => $taskItem['task__plan_start_time'],
                        'end_time' => $taskItem['task__plan_end_time'],
                        'row_type' => 'task',
                    ];
                    StepService::formatGanttItem($tmpChildrenTask);
                    $childrenTask[] = $tmpChildrenTask;
                }
                $entityData['children'] = $childrenTask;
                $res[] = $entityData;
            }
        }
        return StepService::formatStepCategoryPlanData($res, []);
    }

    /**
     * 后台-排班管理列表
     * @param $param
     * @param $tenantId
     * @return array
     */
    public function getScheduleManageList($param, $tenantId)
    {
        $startTime = $param['start_time'];
        $endTime = $param['end_time'];
        $scheduleList = model(ScheduleManageModel::class)
            ->where(['schedule_time' => ['BETWEEN', [$startTime, $endTime]]])
            ->where(['tenant_id' => $tenantId])
            ->select();
        if (empty($scheduleList)) {
            return [];
        }
        return $scheduleList;
    }

    /**
     * 后台-创建排期管理
     * @param $param
     * @return array
     * @throws Exception
     */
    public function createScheduleManageData($param)
    {
        // 批量导入排期
        $dataList = $param['data'] ?? [];
        $saveData = [];
        $updateData = [];
        foreach ($dataList as $item) {
            $item['schedule_time'] = strtotime($item['time']);
            if (empty($item['id'])) {
                $saveData[] = $item;
            }
            if (!empty($item['id'])) {
                $updateData[] = $item;
            }
        }
        $successData = [];
        $failData = [];
        $commonService = CommonService::instance('scheduleManage');
        $editProjectModule = module_by_code('schedule_manage');
        // 没有id,创建
        if (!empty($saveData)) {
            foreach ($saveData as $data) {
                try {
                    $successData[] = $commonService->commonCreate(['data' => $data], $editProjectModule);
                } catch (\Exception $e) {
                    switch ($e->getCode()) {
                        case 10501: // 重复的覆盖
                            $where['schedule_time'] = strtotime($data['time']);
                            if (!empty($data['project_id'])) {
                                $where['project_id'] = $data['project_id'];
                            }
                            model(ScheduleManageModel::class)->where($where)->save([
                                'is_work' => $data['is_work'],
                                'week_type' => $data['week_type'],
                            ]);
                            break;
                        default:
                            $failData[] = [
                                'code' => $e->getCode(),
                                'message' => $data['time'] . '存在异常数据',
                                'info' => $e->getMessage()
                            ];
                    }
                }
            }
        }
        // 有id,编辑
        if (!empty($updateData)) {
            foreach ($updateData as $data) {
                try {
                    $successData[] = $commonService->commonUpdate(['data' => $data], $editProjectModule, false);
                } catch (\Exception $e) {
                    $failData[] = [
                        'code' => $e->getCode(),
                        'message' => $e->getMessage()
                    ];
                }
            }
        }
        return ['success_list' => $successData, 'fail_list' => $failData];
    }

    /**
     * 后台-排班管理列表(one)
     * @param $param
     * @param $tenantId
     * @return array
     */
    public function getScheduleManageOne($param, $tenantId)
    {
        $type = $param['type'] ?? 'default';
        $projectId = $param['project_id'] ?? 0;
        $isWork = $param['is_work'] ?? 'default';
        $scheduleList = model(ScheduleManageModel::class)->cache(60)->field('id,title,is_work,type,title,schedule_time')
            ->where(['type' => $type, 'is_work' => $isWork])
            ->where(['tenant_id' => $tenantId, 'project_id' => $projectId])
//            ->where(['schedule_time' => ['BETWEEN', [$startTime, $endTime]]])
            ->select();
        // 查询默认排班
        if (empty($scheduleList)) {
            $scheduleList = model(ScheduleManageModel::class)->cache(60)->field('id,title,is_work,type,title,schedule_time')
                ->where(['type' => 'default', 'is_work' => $isWork])
                ->where(['tenant_id' => $tenantId, 'project_id' => 0])
                ->select();
        }
        if (empty($scheduleList)) {
            return [];
        }
        return array_map(function ($tmp) {
            $tmp['date'] = date('Y-m-d', $tmp['schedule_time']);
            $tmp['title'] = $tmp['schedule_time'] ?? '通用排班';
            return $tmp;
        }, $scheduleList);
    }
}
